﻿using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;

namespace CommLib.Compress
{
    public static class Zip
    {
        /// <summary>
        /// 创建压缩包
        /// </summary>
        /// <param name="zipSaveFilePath">压缩包保存的路径</param>
        /// <param name="filePath">要压缩文件的路径</param>
        /// <param name="fileSaveName">保存到压缩包内的名（不填为原名,可填层级(\分割)）</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <exception cref="System.Security.SecurityException"></exception>
        /// <returns></returns>
        public static async Task<Tuple<bool, string>> ZipCreate(string zipSaveFilePath, string filePath, string fileSaveName = "")
        {
            if(string.IsNullOrEmpty(zipSaveFilePath))
                return new Tuple<bool, string>(false, "压缩包保存的路径不能为空");
            if (File.Exists(zipSaveFilePath))
                return new Tuple<bool, string>(false, $"已有此{zipSaveFilePath}压缩包");
            if (!File.Exists(filePath))
                return new Tuple<bool, string>(false, "没有可以压缩的文件");
            using (var zipFileToOpen = new FileStream(zipSaveFilePath, FileMode.Create))
            {
                using (var archive = new ZipArchive(zipFileToOpen, ZipArchiveMode.Create))
                {
                    fileSaveName = SetFileSaveName(filePath, fileSaveName);
                    var readMeEntry = archive.CreateEntry(fileSaveName);
                    using (var stream = readMeEntry.Open())
                    {
                        var bytes = File.ReadAllBytes(filePath);
                        await stream.WriteAsync(bytes, 0, bytes.Length);
                    }
                }
            }
            return new Tuple<bool, string>(true, "压缩包创建成功");
        }

        /// <summary>
        /// 创建压缩包
        /// </summary>
        /// <param name="zipSaveFilePath">压缩包保存的路径</param>
        /// <param name="filePath">要压缩文件的路径</param>
        /// <param name="fileSaveName">保存到压缩包内的名（不填为原名,可填层级(\分割)）</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <returns></returns>
        public static Tuple<bool, string> ZipFileCreate(string zipSaveFilePath, string filePath, string fileSaveName = "")
        {
            if (string.IsNullOrEmpty(zipSaveFilePath))
                return new Tuple<bool, string>(false, "压缩包保存的路径不能为空");
            if (File.Exists(zipSaveFilePath))
                return new Tuple<bool, string>(false, $"已有此{zipSaveFilePath}压缩包");
            if (!File.Exists(filePath))
                return new Tuple<bool, string>(false, $"没有可以压缩的文件");
            using (var zipArchive = ZipFile.Open(zipSaveFilePath, ZipArchiveMode.Create))
            {
                fileSaveName = SetFileSaveName(filePath, fileSaveName);
                zipArchive.CreateEntryFromFile(filePath, fileSaveName);
            }
            return new Tuple<bool, string>(true, "压缩包创建成功");
        }

        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="zipSaveFilePath">压缩包保存的路径</param>
        /// <param name="directoryPath">要压缩的文件夹</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        public static Tuple<bool, string> ZipCompressedDirectory(string zipSaveFilePath, string directoryPath)
        {
            if (File.Exists(zipSaveFilePath))
                return new Tuple<bool, string>(false, $"已有此{zipSaveFilePath}压缩包");
            if (!Directory.Exists(directoryPath))
                return new Tuple<bool, string>(false, "没有可以压缩的文件夹");
            ZipFile.CreateFromDirectory(directoryPath, zipSaveFilePath);
            return new Tuple<bool, string>(true, "压缩包创建成功");
        }

        /// <summary>
        /// 解压整个包
        /// </summary>
        /// <param name="zipFilePath">压缩包路径</param>
        /// <param name="directoryPath">要解压到保存的路径</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        public static Tuple<bool, string> ZipUnCompressedDirectory(string zipFilePath, string directoryPath)
        {
            if (!File.Exists(zipFilePath))
                return new Tuple<bool, string>(false, $"没有此{zipFilePath}压缩包");
            if (!Directory.Exists(directoryPath))
                return new Tuple<bool, string>(false, $"没有此{directoryPath}文件路径");
            ZipFile.ExtractToDirectory(zipFilePath, directoryPath);
            return new Tuple<bool, string>(true, "解压成功");
        }

        /// <summary>
        /// 更新压缩包
        /// </summary>
        /// <param name="zipFilePath">压缩包路径</param>
        /// <param name="filePath">要添加到压缩包的文件路径</param>
        /// <param name="fileSaveName">保存到压缩包名（不填为原名,可填层级(\分割)）</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <exception cref="System.Security.SecurityException"></exception>
        /// <returns></returns>
        public static async Task<Tuple<bool, string>> ZipUpdate(string zipFilePath, string filePath, string fileSaveName = "")
        {
            if (!File.Exists(zipFilePath))
                return new Tuple<bool, string>(false, $"没有此{zipFilePath}压缩包");
            using (var zipFileToOpen = new FileStream(zipFilePath, FileMode.Open))
            {
                using (var archive = new ZipArchive(zipFileToOpen, ZipArchiveMode.Update))
                {
                    fileSaveName = SetFileSaveName(filePath, fileSaveName);
                    var readMeEntry = archive.CreateEntry(fileSaveName);
                    using (var stream = readMeEntry.Open())
                    {
                        var bytes = File.ReadAllBytes(filePath);
                        await stream.WriteAsync(bytes, 0, bytes.Length);
                    }
                }
            }
            return new Tuple<bool, string>(true, "更新压缩包成功");
        }

        /// <summary>
        /// 提取压缩包单个文件
        /// </summary>
        /// <param name="zipFilePath">压缩包路径</param>
        /// <param name="fileName">要提取的文件名</param>
        /// <param name="fileSavePath">文件要保存的位置</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <exception cref="System.Security.SecurityException"></exception>
        /// <returns></returns>
        public static Tuple<bool, string> ZipUnCompressedFile(string zipFilePath, string fileName, string fileSavePath)
        {
            if (!File.Exists(zipFilePath))
                return new Tuple<bool, string>(false, $"没有此{zipFilePath}压缩包");
            if(string.IsNullOrEmpty(fileSavePath))
                return new Tuple<bool, string>(false, "保存的路径不能为空");

            using (var zipFileToOpen = new FileStream(zipFilePath, FileMode.Open))
            {
                using (var archive = new ZipArchive(zipFileToOpen, ZipArchiveMode.Read))
                {
                    var entry = archive.GetEntry(fileName);
                    if (entry == null)
                        return new Tuple<bool, string>(false, $"压缩包内没有此{fileName}文件");
                    using (var stream = entry.Open())
                    {
                        fileSavePath = string.IsNullOrEmpty(Path.GetExtension(fileSavePath))
                            ? fileSavePath + Path.GetExtension(fileName)
                            : fileSavePath;
                        var output = new FileStream(fileSavePath, FileMode.Create);
                        var b = -1;
                        while ((b = stream.ReadByte()) != -1)
                        {
                            output.WriteByte((byte)b);
                        }
                        output.Close();
                    }
                }
            }
            return new Tuple<bool, string>(true, "文件提取成功");
        }

        /// <summary>
        /// 删除压缩包单个文件
        /// </summary>
        /// <param name="zipFilePath">压缩包路径</param>
        /// <param name="fileName">要提取的文件名</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="InvalidDataException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <exception cref="System.Security.SecurityException"></exception>
        /// <returns></returns>
        public static Tuple<bool, string> ZipDeletefile(string zipFilePath, string fileName)
        {
            if (!File.Exists(zipFilePath)) return new Tuple<bool, string>(false, $"没有此{zipFilePath}压缩包");

            using (var zipFileToOpen = new FileStream(zipFilePath, FileMode.Open))
            {
                using (var archive = new ZipArchive(zipFileToOpen, ZipArchiveMode.Update))
                {
                    var entry = archive.GetEntry(fileName);
                    if (entry == null)
                        return new Tuple<bool, string>(false, $"压缩包内没有此{fileName}文件");
                    entry.Delete();
                }
            }
            return new Tuple<bool, string>(true, "文件删除成功");
        }

        private static string SetFileSaveName(string filePath, string fileSaveName)
            => string.IsNullOrEmpty(fileSaveName)? Path.GetFileName(filePath)
                : (string.IsNullOrEmpty(Path.GetExtension(fileSaveName)) ? fileSaveName + Path.GetExtension(filePath) : fileSaveName);
    }
}
